/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sirtet.tetris;

import java.util.HashSet;
import java.util.Set;
import sirtet.GameAction;
import sirtet.GameBoard;
import sirtet.event.BoardActionEvent;
import sirtet.event.BoardBlocksEliminatedEvent;
import sirtet.event.BoardEvent;
import sirtet.event.BoardListener;
import sirtet.event.BoardPieceEvent;
import sirtet.event.BoardPieceFixedEvent;
import sirtet.netplay.NetworkPlayer;
import sirtet.netplay.ServerResponse;
import sirtet.netplay.server.GameRoom;
import sirtet.tetris.gui.fancy.FancyFrame;

/**
 * A GameRoom for Battling Super Tetris
 * @author guangwei.zhu
 */
public class TetrisRoom extends GameRoom {

    public static int BOARD_WIDTH = 20;
    public static int BOARD_HEIGHT = 30;
    private BoardListener gameoverHandler = new BoardListener() {

        public void boardChanged(BoardEvent evt) {
        }

        public void boardDisposing(BoardEvent evt) {
        }

        public synchronized void boardGameOver(BoardEvent evt) {

            GameBoard board = (GameBoard) evt.getSource();
            Set<NetworkPlayer> players = getPlayers();
            Set<GameBoard> boards = getBoards();
            GameAction action = new GameAction();

            for (NetworkPlayer player : players) {
                if (seatTable.get(player).equals(board)) {
                    action.actor = player.id();
                    action.command = TetrisAction.FALL;   // Send the last fall
                    broadcast(action);
                    action.command = ServerResponse.GAMEOVER;
                    broadcast(action);
                }
            }

            for (GameBoard iboard : boards) {
                if (iboard.isAlive()) {
                    return;     // Someone is still playing happily...
                }
            }

            // Everyone is dead now
            for (NetworkPlayer player : players) {
                action.actor = player.id();
                if (seatTable.get(player).equals(board)) {
                    action.command = ServerResponse.WIN;
                } else {
                    action.command = ServerResponse.LOSE;
                }
                broadcast(action);
            }
        }

        public void boardStarted(BoardEvent evt) {
        }

        public void boardReset(BoardEvent evt) {
        }

        public void boardPieceFixed(BoardPieceFixedEvent evt) {
        }

        public void boardNextPieceChanged(BoardPieceEvent evt) {
        }

        public void boardBlocksEliminated(BoardBlocksEliminatedEvent evt) {
        }

        public void boardActionTriggered(BoardActionEvent evt) {
        }
    };

    // This class relays attack to the remote clients
    private class RemoteBattlingDelegate implements BoardAttackListener {

        private NetworkPlayer player;

        public RemoteBattlingDelegate(NetworkPlayer player) {
            this.player = player;
        }

        public void BoardAttacked(BoardAttackedEvent evt) {
            Object[][] attackBlocks = evt.blocks;
            StringBuilder strb;

            for (Object[] attackLine : attackBlocks) {
                strb = new StringBuilder();
                for (Object attackBlock : attackLine) {
                    strb.append(attackBlock.toString());
                }
                broadcast(new GameAction(player.id(), BattlingTetrisBoard.MSG_ATTACK + strb.toString()));
            }
        }
    }

    @Override
    protected void playerQuit(NetworkPlayer player) {
        BattlingTetrisBoard oldboard = (BattlingTetrisBoard) seatTable.get(player);

        super.playerQuit(player);
        oldboard.clearAttackTarget();

        for (GameBoard board : this.getBoards()) {
            ((BattlingTetrisBoard) board).removeAttackTarget(oldboard);
        }
        // TODO check Concurrent
        // oldboard.dispose();
    }

    @Override
    protected GameBoard createBoard(NetworkPlayer player) {
        BattlingTetrisBoard newBoard = new BattlingTetrisBoard(BOARD_WIDTH, BOARD_HEIGHT);
        Set<BattlingTetrisBoard> boards = new HashSet<BattlingTetrisBoard>();

        // Convert set type
        for (GameBoard board : this.getBoards()) {
            boards.add((BattlingTetrisBoard) board);
        }

        // build relation among all battling boards
        for (BattlingTetrisBoard board : boards) {
            board.addAttackTarget(newBoard);
            newBoard.addAttackTarget(board);
        }

        // Add remote attack target
        newBoard.addBoardAttackListener(new RemoteBattlingDelegate(player));

        // Add remote piece provider
        newBoard.setPieceProvider(new SynchronousPieceProvider(new RandomPieceProvider(11), player));

        // Add fall enforcer
        newBoard.registerPlayer(new FallEnforcer(player.id(), 300));

        // Add gameover listener
        newBoard.addBoardListener(gameoverHandler);

        new FancyFrame(newBoard, player).setVisible(true);

        return newBoard;
    }

    @Override
    public void setMaxBoards(int maxBoards) {
        // One board for one player, so..
        super.setMaxBoards(maxBoards);
        super.setMaxPlayers(maxBoards);
    }

    @Override
    public void setMaxPlayers(int maxPlayers) {
        // One board for one player, so..
        super.setMaxBoards(maxPlayers);
        super.setMaxPlayers(maxPlayers);
    }
}
